package net.sf.xoperator.blackboard;

import java.util.List;
import java.util.Set;

import net.sf.xoperator.configuration.KSConfiguration;
import net.sf.xoperator.configuration.PersistentKSConfiguration;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openbbs.blackboard.filter.KindOfFilter;
import org.openbbs.blackboard.ks.KSExecutionContext;
import org.openbbs.blackboard.ks.KnowledgeSource;
/**
 * Base class for all the <code>KnowledgeSources</code> that make use of the configuration faclities of the agent. This class provides to functionalities. First it will say, that it is enabled for any configuration object and  second will call the <code>configure(AgentConfiguration conf)</code> method when executing. 
 * @author joerg
 *
 */
public abstract class ConfigurationAwareKnowledgeSource implements
		KnowledgeSource {
   
   /**
    * this constructor reads in the configuration class defined by the extending classes
    *
    */
   public ConfigurationAwareKnowledgeSource() {
      this.confs = getConfigurationClasses();
   }
	
	private Log log = LogFactory.getLog(ConfigurationAwareKnowledgeSource.class);

   private List<Class<? extends KSConfiguration>> confs;

	public void execute(KSExecutionContext context) {
      if (context.blackboard().exists(new KindOfFilter(KSConfiguration.class))) {
         
         for (Class<? extends KSConfiguration> conf : this.confs) {
            for (Object obj : context.blackboard().readAll(new KindOfFilter(conf))) {
               try {
                  configure((KSConfiguration) obj);
               } catch (Exception e) {
                  this.log.error("An error occured while configuring the KS: ",e);
                  throw(new RuntimeException(e));
               }
            }
            
         }
      }else{
			try{
				executeInternal(context);
			}catch(Exception e){
				this.log.error("An error occured while in the executeInternal: ",e);
				throw(new RuntimeException(e));
			}
		}

	}
	/**
	 * checks for <code>AgentConfiguration</code> obejct. If none is found the value of isEnabledInternal is returned.
	 */
	public boolean isEnabled(KSExecutionContext context) {
		
		Set<Object> confs =  context.blackboard().readAll(new KindOfFilter(KSConfiguration.class));
		
		for (Object conf : confs) {
			Class fromTheContext = conf.getClass();
			for (Object object : getConfigurationClasses()) {
				Class configurationClass = (Class) object;
				if(configurationClass.isAssignableFrom(fromTheContext)){
					return true;
				}
			}
		}
		return isEnabledInternal(context);
		
		
	}

	/**
	 * override this to put the non-Configuration related code here
	 */
	public abstract void executeInternal(KSExecutionContext context);

	/**
	 * override to this for configuring the agent. will be called at startup and
	 * whenever the configuration changes.
	 * 
	 * @param config
	 * @throws Exception if an exception is thrown by an implementing class, the agent will be halted, as it needs a working configuration.
	 */
	public abstract void configure(KSConfiguration config) throws Exception;
	
	/**
	 * implement here the non-Configuration related code here.
	 */
	public abstract boolean isEnabledInternal(KSExecutionContext context);
   

   /**
    * 
    * @return a list with all the classes that configure this knowledge source
    */
   public abstract List<Class<? extends KSConfiguration>> getConfigurationClasses() ;
}
